/**
 * Copyright 2010 Jake Jackson
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jake.nb.ext.ant.completion;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;

import org.apache.tools.ant.module.api.AntProjectCookie;
import org.apache.tools.ant.module.api.support.TargetLister;
import org.apache.tools.ant.module.api.support.TargetLister.Target;

import org.jake.nb.ext.ant.parser.AntParser;

import org.netbeans.spi.editor.completion.CompletionProvider;
import org.netbeans.spi.editor.completion.CompletionResultSet;
import org.netbeans.spi.editor.completion.CompletionTask;
import org.netbeans.spi.editor.completion.support.AsyncCompletionQuery;
import org.netbeans.spi.editor.completion.support.AsyncCompletionTask;
import org.openide.util.Exceptions;

/**
 *
 * @author JakeJ
 */
@org.openide.util.lookup.ServiceProvider(service=CompletionProvider.class, position=0)
public class AntTargetCompletionProvider implements CompletionProvider {

  public static final Collection<String> STOPPER_STRINGS = Arrays.asList(new String[]{"\"", ","}); // NOI18N

  /**
   * Dummy constructor to get access via layers.
   */
  public AntTargetCompletionProvider() {
  }

  public CompletionTask createTask(final int queryType, final JTextComponent component) {
    CompletionTask retValue = null;
    if (queryType == CompletionProvider.COMPLETION_QUERY_TYPE) {
      retValue = new AsyncCompletionTask(new AsyncCompletionQuery() {

        @Override
        protected void query(final CompletionResultSet resultSet, final Document document, final int offset) {
          if (document != null) {
            final AntProjectCookie projectCookie = AntParser.getProjectCookie(document, offset);
            if (projectCookie != null) {
              Set<Target> targets = null;
              try {
                targets = TargetLister.getTargets(projectCookie);
              } catch (IOException ex) {
                Exceptions.printStackTrace(ex);
              }
              if (targets != null) {
                // obtain the targets available to be inserted at the offset position
                final Set<Target> targetsToDisplay = getTargetsToDisplay(document, offset);

                if (!targetsToDisplay.isEmpty()) {
                  int startOffset = getStartOffset(document, offset);
                  String filter = ""; // NOI18N
                  if (startOffset <= offset) {
                    try {
                      // get the text from the stopper string to the current cursor position
                      final String untrimmedText = document.getText(startOffset, offset - startOffset);
                      // now get a string to compare with the target names
                      filter = untrimmedText.trim();
                      if (filter.length() > 0) {
                        startOffset += untrimmedText.indexOf(filter);
                      }
                    } catch (BadLocationException ex) {
                      Exceptions.printStackTrace(ex);
                    }
                  }

                  // the complete buildorder
                  final Collection<Target> sortForDisplay = getTargetsForFullName(targetsToDisplay, document, offset);
                  for (final Target target : targetsToDisplay) {
                    if (filter.length() == 0 || (filter.length() > 0
                            && (target.getName().startsWith(filter) || target.getQualifiedName().startsWith(filter)))) {
                      //                      AntParser.getFileForTargetName(document, filter, offset)
                      resultSet.addItem(new AntTargetCompletionItem(target, startOffset, sortForDisplay.contains(target)));
                    }
                  }
                }
              }
            }
          }
          resultSet.finish();
        }

        /**
         * Get the targets which needs to be displayed with their qualified name.
         */
        private Collection<Target> getTargetsForFullName(final Set<Target> targetsToDisplay, final Document document, int offset){
          final Collection<Target> retSet = new HashSet<Target>();
          final Map<String, String> map = new HashMap<String, String>();
          for (final org.apache.tools.ant.Target target :  AntParser.getSupposedBuildOrder(document, "", offset)) { // NOI18N
            map.put(target.getName(), target.getLocation().getFileName());
          }


          for (final Target target : targetsToDisplay) {
            final String fileName = map.get(target.getName());
            final String targetsFile = target.getScript().getFile().getAbsolutePath();
            if(!fileName.equals(targetsFile)) {
              retSet.add(target);
            }
          }
          return retSet;
        }

        private Set<Target> getTargetsToDisplay(final Document document, final int offset) {
          final Set<Target> possibleTargets = AntParser.getPossibleTargets(document, offset);
          // we have two different calls to AntParser. this returns two different target objects - so the only
          // way to sort out is the name comparison
          final Map<String, Target> nameToTarget = new HashMap<String, Target>();
          for (final Target target : possibleTargets) {
            nameToTarget.put(target.getQualifiedName(), target);
          }

          for (final Target target : AntParser.getUsedTargets(document, offset)) {
            if (nameToTarget.containsKey(target.getQualifiedName())) {
              possibleTargets.remove(nameToTarget.get(target.getQualifiedName()));
            }
          }

          return possibleTargets;
        }

        /**
         * Get the offset to start insertion at. Passed {@code offset} is the value where the cursor is actually
         * located, but we need the position of the previous comma (,) or quote (").
         */
        private int getStartOffset(final Document document, final int offset) {
          int startOffset = offset;
          int index = offset - 1;
          do {
            try {
              final String character = document.getText(index, 1);
              if (STOPPER_STRINGS.contains(character)) {
                // found - take the index and add one to position the cursor next to the stopper
                startOffset = index + 1;
                break;
              }
              index--;
            } catch (BadLocationException ex) {
              Exceptions.printStackTrace(ex);
            }
          } while (index > 0);
          return startOffset;
        }
      }, component);
    }
    return retValue;
  }
 
  public int getAutoQueryTypes(final JTextComponent component, final String typedText) {
    return 0;
  }
}
